﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI;

namespace Pegasus.Web
{
    /// <summary>
    /// Control that registers global vars of server control ClientIds for use in Javascript.
    /// </summary>
    /// <remarks>
    /// This control should be placed on the page before the ScriptManager.
    /// Once placed on the page it can be used statically by all controls.
    /// </remarks>
    public class GlobalVarManager : Control
    {
        private Dictionary<string, string> m_globalScripts;
        private IDictionary<string, string> m_globalVars;
        private bool m_sortVars = false;

        /// <summary>
        /// Whether the global variables should be in an alphabetical list.
        /// </summary>
        public bool SortVars
        {
            get
            {
                return m_sortVars;
            }
            set
            {
                m_sortVars = value;
            }
        }

        /// <summary>
        /// Allows a control to have it's ClientId put onto the page as a global variable.
        /// I.E. if the control's ID is button1 then the following javascript is added to the page:
        /// var button1 = 'ctl01_......_button1';
        /// </summary>
        /// <param name="control">The control to register as a global variable.</param>
        public static void RegisterControl( Control control )
        {
            RegisterControl( control, null );
        }

        /// <summary>
        /// Same as the other version except you can specify the key name.
        /// </summary>
        /// <param name="control">The control.</param>
        /// <param name="varName">The name of the global variable.</param>
        public static void RegisterControl( Control control, string varName )
        {
            // Make sure control is valid with valid page.
            if( control == null )
            {
                throw new ArgumentNullException( "control" );
            }
            if( control.Page == null )
            {
                throw new ArgumentNullException( "The control is not on the page." );
            }

            // Add control to list for control's page's ScriptManagerWithVars
            GlobalVarManager current = GetCurrent( control.Page ) as GlobalVarManager;
            if( current == null )
            {
                throw new InvalidOperationException( "There must be a GlobalVarManager on the page." );
            }

            if( string.IsNullOrEmpty( varName ) )
            {
                varName = control.ID;
            }

            if( !current.m_globalVars.ContainsKey( varName ) )
            {
                current.m_globalVars.Add( varName, control.ClientID );
            }
        }

        /// <summary>
        /// Allows a javascript block to be placed after the global vars on the page.
        /// </summary>
        /// <param name="uniqueKey">A unique key to prevent duplicate scripts.</param>
        /// <param name="page">The page on which to register the script.</param>
        /// <param name="script">The javascript to register.</param>
        public static void RegisterScript( Page page, string uniqueKey, string script )
        {
            if( page == null )
            {
                throw new ArgumentNullException( "page" );
            }

            // Get manager and make sure it exists on page.
            GlobalVarManager current = GetCurrent( page ) as GlobalVarManager;
            if( current == null )
            {
                throw new InvalidOperationException( "There must be a GlobalVarManager on the page." );
            }

            current.RegisterScript( uniqueKey, script );
        }

        /// <summary>
        /// Allows a javascript block to be placed after the global vars on the page.
        /// </summary>
        /// <param name="page">The page on which to register the script.</param>
        /// <param name="uniqueKey">A unique key to prevent duplicate scripts.</param>
        /// <param name="script">The javascript block format string to register.</param>
        /// <param name="args">The args for the format string.</param>
        public static void RegisterScript( Page page, string uniqueKey, string script, params object[] args )
        {
            RegisterScript( page, uniqueKey, string.Format( script, args ) );
        }

        /// <summary>
        /// Same as the other versions except you specify the name and value of the var.
        /// </summary>
        /// <param name="page">The page on which the var belongs.</param>
        /// <param name="name">The name of the var.</param>
        /// <param name="value">The value of the var.  It is assumed that the value will be a Javascript string with formatting needed.</param>
        /// <param name="args">The args for the format string.</param>
        public static void RegisterVar( Page page, string name, object value, params string[] args )
        {
            if( page == null )
            {
                throw new ArgumentNullException( "page" );
            }

            // Add control to list for control's page's ScriptManagerWithVars
            GlobalVarManager current = GetCurrent( page ) as GlobalVarManager;
            if( current == null )
            {
                throw new InvalidOperationException( "There must be a GlobalVarManager on the page." );
            }

            current.RegisterVar( name, string.Format( value.ToString(), args ) );
        }

        /// <summary>
        /// Allows a javascript block to be placed after the global vars on the page.
        /// </summary>
        /// <param name="uniqueKey">A unique key to prevent duplicate scripts.</param>
        /// <param name="script">The javascript to register.</param>
        public void RegisterScript( string uniqueKey, string script )
        {
            if( string.IsNullOrEmpty( script ) )
            {
                throw new ArgumentNullException( "script" );
            }

            if( !m_globalScripts.ContainsKey( uniqueKey ) )
            {
                m_globalScripts.Add( uniqueKey, script );
            }
        }

        /// <summary>
        /// Allows a javascript block to be placed after the global vars on the page.
        /// </summary>
        /// <param name="uniqueKey">A unique key to prevent duplicate scripts.</param>
        /// <param name="script">The javascript block format string to register.</param>
        /// <param name="args">The args for the format string.</param>
        public void RegisterScript( string uniqueKey, string script, params object[] args )
        {
            RegisterScript( uniqueKey, string.Format( script, args ) );
        }

        /// <summary>
        /// Same as the other versions except you specify the name and value of the var.
        /// </summary>
        /// <param name="name">The name of the var.</param>
        /// <param name="value">The value of the var.  It is assumed that the value will be a Javascript string.</param>
        public void RegisterVar( string name, object value )
        {
            if( !m_globalVars.ContainsKey( name ) )
            {
                m_globalVars.Add( name, value.ToString() );
            }
        }

        /// <summary>
        /// Initialization code.
        /// </summary>
        /// <param name="e">The event.</param>
        protected override void OnInit( EventArgs e )
        {
            base.OnInit( e );

            if( DesignMode )
            {
                return;
            }

            if( GetCurrent( Page ) != null )
            {
                throw new InvalidOperationException( "There can only be one GlobalVarManager per page." );
            }

            if( m_sortVars )
            {
                m_globalVars = new SortedDictionary<string, string>();
            }
            else
            {
                m_globalVars = new Dictionary<string, string>();
            }
            m_globalScripts = new Dictionary<string,string>();

            Page.Items[ typeof( GlobalVarManager ) ] = this;
        }

        protected override void OnPreRender( EventArgs e )
        {
            // Build up list of global vars.
            StringBuilder sb = new StringBuilder();
            foreach( KeyValuePair<string,string> pair in m_globalVars )
            {
                sb.AppendLine( string.Format( "var {0} = '{1}';", pair.Key, pair.Value ) );
            }

            // Add all global scripts.
            foreach( string script in m_globalScripts.Values )
            {
                sb.AppendLine( script );
            }

            if( sb.Length == 0 )
            {
                return;
            }

            // Register script.
            ScriptManager scriptManager = ScriptManager.GetCurrent( Page );
            Type type = GetType();
            if( scriptManager == null || !scriptManager.IsInAsyncPostBack )
            {
                // Use ClientScript if the request isn't an Ajax postback.
                Page.ClientScript.RegisterStartupScript( type, type.FullName, sb.ToString(), true );
            }
            else
            {
                ScriptManager.RegisterStartupScript( Page, type, type.FullName, sb.ToString(), true );
            }
        }

        private static GlobalVarManager GetCurrent( Page page )
        {
            if( page == null )
            {
                throw new ArgumentNullException( "page" );
            }

            return page.Items[ typeof( GlobalVarManager ) ] as GlobalVarManager;
        }
    }
}